package com.tianji.learning.service.impl;

import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.task.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author hzy
 * @since 2023-09-07
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService learningLessonService;

    private final CourseClient courseClient;

    private final LearningRecordDelayTaskHandler taskHandler;

    /**
     * 查询指定课程的学习记录
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO selectLessonRecord(Long courseId) {
        Long userId = UserContext.getUser();
        //查询课表id
        LearningLesson lesson = learningLessonService.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .one();
        if (lesson == null) {
            return null;
        }


        //获取学习记录信息
        List<LearningRecord> recordList = lambdaQuery().eq(LearningRecord::getLessonId, lesson.getId())
                .eq(LearningRecord::getUserId, userId)
                .list();

        List<LearningRecordDTO> recordDTOS = BeanUtils.copyList(recordList, LearningRecordDTO.class);
        //封装信息
        LearningLessonDTO lessonDTO = new LearningLessonDTO();
        lessonDTO.setId(lesson.getId());
        lessonDTO.setLatestSectionId(lesson.getLatestSectionId());
        lessonDTO.setRecords(recordDTOS);

        return lessonDTO;
    }

    /**
     * 提交学习记录
     *
     * @param dto
     */
    @Override
    @Transactional
    public void commitRecord(LearningRecordFormDTO dto) {
        Long userId = UserContext.getUser();

        //查询课表
        LearningLesson lesson = learningLessonService.lambdaQuery().eq(LearningLesson::getId, dto.getLessonId()).one();
        if (lesson == null) {
            throw new DbException("课表信息不存在!!!");
        }

        //判断是考试 or 视频
        if (dto.getSectionType() == SectionType.EXAM) {
            handlerExam(userId, dto, lesson);
        } else {
            handlerVideo(userId, dto, lesson);
        }
    }

    //处理视频
    private void handlerVideo(Long userId, LearningRecordFormDTO dto, LearningLesson lesson) {
        //判断是不是第一次学习
        LearningRecord old = queryLearningRecord(dto);

        if (old == null) {
            //新增学习记录
            LearningRecord learningRecord = BeanUtils.copyBean(dto, LearningRecord.class);
            learningRecord.setUserId(userId);
            boolean save = save(learningRecord);
            if (!save) {
                throw new DbException("新增学习记录失败!!!");
            }
            //修改课表
            handlerLesson(dto, lesson);
            return;
        }
        //不是第一次学,判断是否超过视频时长一半,并且未学习
        boolean ok = !old.getFinished() && dto.getMoment() * 2 >= dto.getDuration();
        if (!ok) {
            //不是第一次学完,学习记录存入redis
            LearningRecord record = new LearningRecord();
            record.setLessonId(dto.getLessonId());
            record.setSectionId(dto.getSectionId());
            record.setMoment(dto.getMoment());  //必须是前端新提交的学习moment, 不能是old(数据库)的
            record.setId(old.getId());
            record.setFinished(old.getFinished());

            //4.1 存到Redis, 提交延迟任务
            taskHandler.addLearningRecordTask(record);
            //4.2结束
            return;
        }
        //第一次学完
        //修改学习记录
        boolean update = lambdaUpdate()
                .eq(LearningRecord::getLessonId, dto.getLessonId())
                .eq(LearningRecord::getSectionId, dto.getSectionId())
                .set(LearningRecord::getMoment, dto.getMoment())
                .set(LearningRecord::getFinished, true)
                .set(LearningRecord::getFinishTime, dto.getCommitTime())
                .update();
        if (!update) {
            throw new DbException("修改学习记录失败!!!");
        }
        //清除redis缓存
        taskHandler.cleanRecordCache(dto.getLessonId(), dto.getSectionId());
        //修改课表
        handlerLesson(dto, lesson);
    }

    //课程修改
    private void handlerLesson(LearningRecordFormDTO dto, LearningLesson lesson) {
        //查询课程
        CourseFullInfoDTO infoDTO = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (infoDTO == null) {
            throw new DbException("课程信息不存在!!!");
        }
        //修改课表,要判断是否是最后一节
        boolean b = learningLessonService.lambdaUpdate()
                .eq(LearningLesson::getId, dto.getLessonId())
                .set(lesson.getLearnedSections() + 1 >= infoDTO.getSectionNum(), LearningLesson::getStatus, LessonStatus.FINISHED)
                .set(LearningLesson::getLearnedSections, lesson.getLearnedSections() + 1)
                .set(LearningLesson::getLatestSectionId, dto.getSectionId())
                .set(LearningLesson::getLatestLearnTime, dto.getCommitTime())
                .update();
        if (!b) {
            throw new DbException("修改课表信息失败!!!");
        }
    }

    private LearningRecord queryLearningRecord(LearningRecordFormDTO dto) {
        //首先判断缓存中有无
        LearningRecord record = taskHandler.readRecordCache(dto.getLessonId(), dto.getSectionId());

        if (record != null) {
            //redis有,就返回
            return record;
        }
        //没有就从mysql查询
        record = lambdaQuery()
                .eq(LearningRecord::getLessonId, dto.getLessonId())
                .eq(LearningRecord::getSectionId, dto.getSectionId())
                .one();
        //存入redis
        if (record != null) {
            taskHandler.addLearningRecordTask(record);
        }
        return record;
    }

    //处理考试情况
    private void handlerExam(Long userId, LearningRecordFormDTO dto, LearningLesson lesson) {
        //新增学习记录
        LearningRecord learningRecord = BeanUtils.copyBean(dto, LearningRecord.class);
        learningRecord.setUserId(userId);
        learningRecord.setFinished(true);
        learningRecord.setFinishTime(dto.getCommitTime());
        boolean save = save(learningRecord);
        if (!save) {
            throw new DbException("新增学习记录失败!!!");
        }

        //修改课表
        boolean update = learningLessonService.lambdaUpdate().eq(LearningLesson::getId, dto.getLessonId())
                .set(LearningLesson::getStatus, LessonStatus.FINISHED)
                .set(LearningLesson::getLearnedSections, lesson.getLearnedSections() + 1)
                .set(LearningLesson::getLatestSectionId, dto.getSectionId())
                .set(LearningLesson::getLatestLearnTime, dto.getCommitTime())
                .update();

        if (!update) {
            throw new DbException("修改课表失败!!!");
        }
    }


}
